#ifndef __U_PATH_SYSTEM_H__
#define __U_PATH_SYSTEM_H__

/***
  u-path-system.hpp path processing
  Copyright (C) 2013  Renweu Gao

  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 ***/

#include "u-base.hpp"
#include "u-thread-system.hpp"
#include <cstring>
#include <iostream>
#include <algorithm>
#include <climits>
#include <vector>
#include <list>
#include <typeinfo>

#ifdef __STD_C11__
#include <mutex>
#include <thread>
#include <condition_variable>
#endif

#ifdef __GNUC__

#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <dirent.h>
#include <cstdlib>

#else
// for Microsoft C/C++ compiler

#endif

namespace u {
#ifdef __STD_C11__
    static std::mutex _mutex;
    static std::condition_variable _cv;

    template <typename T, class file_op>
        void async_walk(u::ws::work_station &ws, const std::string &root, file_op op, T &user_data, const std::string &parent = std::string());
#endif

#ifndef __STD_C11__
    bool count(const std::string &root, const std::string &parent, const std::string &name, unsigned char flag, size_t &_size) {
        _size += 1;
        return true;
    }
#endif

    template <typename T, class file_op>
        bool walk(size_t threads, const std::string &root, file_op op, T &user_data, const std::string &parent = std::string());

    template <typename T, class file_op>
        bool rsync_walk(const std::string &root, file_op op, T &user_data, const std::string &parent = std::string());

    class path {
        private:
            char * _suffix;
            char * _prefix;
            char * _file;


        public:

            path(const std::string & _path) {
                assert(!_path.empty());
                size_t ps_pos = std::string::npos;
                size_t fs_pos = std::string::npos;
                ps_pos = _path.rfind(SYSTEM_PATH_SEPARATOR);
                fs_pos = _path.rfind(".");
                _file = NULL;
                _prefix = NULL;
                _suffix = NULL;

                if (fs_pos != std::string::npos && ps_pos != std::string::npos) {
                    if (fs_pos < ps_pos + 1)
                        fs_pos = std::string::npos;
                }

                if (fs_pos != std::string::npos) {
                    _suffix = u::string_dup(
                            _path.substr(fs_pos + 1, (_path.size() - fs_pos)));
                } else {
                    fs_pos = _path.size();
                }

                if (ps_pos != std::string::npos) {
                    if (ps_pos != 0)
                        _prefix = u::string_dup(_path.substr(0, ps_pos));
                    ++ps_pos;
                } else {
                    ps_pos = 0;
                }

                _file = u::string_dup(_path.substr(ps_pos, fs_pos - ps_pos));
            }

            ~path() {
                release();
            }

            /*get the private member, part should be {"prefix","file","suffix"}*/
            const char *get(const std::string &part) {
                if (part == "suffix")
                    return _suffix;
                else if (part == "prefix")
                    return _prefix;
                else if (part == "file")
                    return _file;
                else
                    return NULL;
            }

            /*get multi-parts of path, flag can be {u::P, u::F, u::S} and the combinations*/
            char *get(unsigned char flag) {
                std::string ret;
                char *ret_value = NULL;
                if ((flag & P) == P) {
                    ret.append(_prefix);
                }
                if ((flag & F) == F) {
                    if (!ret.empty()) {
                        ret.append(SYSTEM_PATH_SEPARATOR);
                    }
                    ret.append(_file);
                }
                if ((flag & S) == S) {
                    if (!ret.empty()) {
                        ret.append(".");
                    }
                    ret.append(_suffix);
                }
                if (!ret.empty())
                    ret_value = string_dup(ret);
                return ret_value;
            }

            /*release memory, mainly the members*/
            void release() {
                u::string_free(&_prefix);
                u::string_free(&_file);
                u::string_free(&_suffix);
            }

            /*check whether @name exists, flag={u::F, u::D}*/
            static bool exists(const std::string &filename, unsigned char flag = u::F) {
                bool found = false;
#ifdef __GNUC__
                if (!filename.empty()) {
                    int status;
                    struct stat st_buf;
                    status = stat(filename.c_str(), &st_buf);
                    if (status == 0) {
                        if ((flag == u::F && S_ISREG(st_buf.st_mode)) || (flag == u::D && S_ISDIR(st_buf.st_mode)))
                            found = true;
                    }
                    /*
                       DIR *dfd;
                       if (flag == u::F) {
                       struct dirent *dp;
                       u::path pip = u::path(filename);
                       std::string dir;
                       const char *prefix = pip.get("prefix");
                       if (prefix == NULL || strlen(prefix) == 0)
                       dir = ".";
                       else
                       dir = prefix;
                       if ((dfd = opendir(dir.c_str())) != NULL) {
                       while ((dp = readdir(dfd)) != NULL) {
                       const char *file = pip.get("file");
                       const char *suffix = pip.get("suffix");
                       std::string name;
                       if (file != NULL) {
                       name.append(file);
                       if (suffix != NULL) {
                       name.append(".");
                       name.append(suffix);
                       }
                       }
                       if (dp->d_name == name) {
                       struct stat st_buf;
                       int status = stat(filename.c_str(), &st_buf);
                       if (S_ISREG(st_buf.st_mode))
                       found = true;
                       break;
                       }
                       }
                       closedir(dfd);
                       }
                       } else {
                       if ((dfd = opendir(filename.c_str())) != NULL) {
                       found = true;
                       closedir(dfd);
                       }
                       }*/
                }
#else

#endif
                return (found);
            }

            /*check whether @name ends with @suffix*/
            static bool end_with(const std::string &filename, const std::string &suffix, bool ignore = false) {
                bool ret = false;
                long sub_len = static_cast<long> (filename.size()) - static_cast<long> (suffix.size());
                if (sub_len >= 0) {
                    std::string sub = filename.substr(static_cast<size_t> (sub_len), suffix.size());
                    std::string _suffix(suffix);
                    if (ignore) {
                        std::transform(sub.begin(), sub.end(), sub.begin(), ::tolower);
                        std::transform(_suffix.begin(), _suffix.end(), _suffix.begin(), ::tolower);
                    }
                    ret = sub == _suffix;
                }
                return ret;
            }

            /*check whether @name begins with @prefix*/
            static bool begin_with(const std::string &filename, const std::string &prefix, bool ignore = false) {
                bool ret = false;
                long len = static_cast<long> (filename.size()) - static_cast<long> (prefix.size());
                if (len >= 0) {
                    std::string sub = filename.substr(0, prefix.size());
                    std::string _prefix(prefix);
                    if (ignore) {
                        std::transform(sub.begin(), sub.end(), sub.begin(), ::tolower);
                        std::transform(_prefix.begin(), _prefix.end(), _prefix.begin(), ::tolower);
                    }
                    ret = sub == _prefix;
                }
                return ret;
            }

            /*combine @first and the latters to path, MUST be ended with NULL*/
            static char * join(const char* first, ...) {
                char *newvalue = NULL;
                if (first != NULL) {
                    std::string ret(first);
                    char *value = NULL;
                    va_list arg;
                    va_start(arg, first);
                    while (true) {
                        value = va_arg(arg, char*);
                        if (value == NULL)
                            break;
                        if (strlen(value) == 1 && strcmp(value, SYSTEM_PATH_SEPARATOR) == 0)
                            continue;
                        if (begin_with(value, SYSTEM_PATH_SEPARATOR)) {
                            if (end_with(ret, SYSTEM_PATH_SEPARATOR)) {
                                ret.append(value + 1);
                            } else {
                                ret.append(value);
                            }
                        } else {
                            if (end_with(ret, SYSTEM_PATH_SEPARATOR)) {
                                ret.append(value);
                            } else {
                                ret.append(SYSTEM_PATH_SEPARATOR);
                                ret.append(value);
                            }
                        }
                    }
                    va_end(arg);
                    newvalue = string_dup(ret);
                }
                return newvalue;
            }

            /*split @name to pieces with max numbers @num if @num not equal to -1 by @spliter*/
            static std::vector<char *> split(const std::string &name, const std::string &spliter = SYSTEM_PATH_SEPARATOR, int num = -1) {
                std::vector<char *> ret;
                std::size_t pos = name.find(spliter);
                std::size_t prev = 0;
                if (num <= 0)
                    num = INT_MAX;
                while (pos != std::string::npos && num > 0) {
                    if (pos - prev != 0) {
                        ret.push_back(string_dup(name.substr(prev, pos - prev)));
                        --num;
                    }
                    prev = pos + spliter.size();
                    pos = name.find(spliter, prev);
                }
                if (num > 0) {
                    ret.push_back(string_dup(name.substr(prev)));
                }
                return ret;
            }

            /*split @name into two part by '.'*/
            static std::pair<char*, char*> splitext(const std::string &name) {
                char* prefix;
                char* suffix;
                std::size_t pos = name.find_last_of(".");
                if (pos == std::string::npos) {
                    suffix = NULL;
                    prefix = string_dup(name);
                } else {
                    prefix = string_dup(name.substr(0, pos));
                    suffix = string_dup(name.substr(pos + 1));
                }
                return std::pair<char*, char*>(prefix, suffix);
            }

            /*append @value to @part={prefix, file, suffix} in @name*/
            static char * append(const std::string &name, const std::string &value, const std::string &part) {
                assert(!name.empty() && !value.empty() && !part.empty());
                std::string ret(name);

                if (part == "suffix") {
                    ret.append(value);
                } else if (part == "prefix") {
                    size_t slash_pos = ret.rfind(SYSTEM_PATH_SEPARATOR);
                    ret.insert(slash_pos + 1, value);
                } else if (part == "file") {
                    size_t dot_pos = name.rfind(".");
                    if (dot_pos != std::string::npos)
                        ret.insert(dot_pos, value);
                }
                return string_dup(ret);
            }

            /*replace @part={'prefix','file','suffix'} of @name with @value*/
            static char * replace(const std::string &name, const std::string &value, const std::string &part) {
                assert(!name.empty());
                size_t ps_pos = std::string::npos;
                size_t fs_pos = std::string::npos;
                ps_pos = name.rfind(SYSTEM_PATH_SEPARATOR);
                fs_pos = name.rfind(".");
                char *ret = NULL;

                if (fs_pos != std::string::npos && ps_pos != std::string::npos) {
                    if (fs_pos < ps_pos + 1)
                        fs_pos = std::string::npos;
                }

                if (fs_pos == std::string::npos) {
                    fs_pos = name.size();
                }

                if (ps_pos == std::string::npos) {
                    ps_pos = 0;
                }

                if (part == "prefix") {
                    ret = u::path::join(value.c_str(), name.substr(ps_pos + 1).c_str(), NULL);
                } else if (part == "file") {
                    char *_path = u::path::join(name.substr(0, ps_pos + 1).c_str(), value.c_str(), NULL);
                    ret = u::string_dup(std::string(_path) + name.substr(fs_pos));
                    u::string_free(&_path);
                } else if (part == "suffix") {
                    ret = u::string_dup(name.substr(0, fs_pos + 1) + value);
                }
                return ret;
            }


            /*count how many ['/', '\'] in @name*/
            static size_t depth(const std::string &name) {
                size_t depth = 1;
                size_t pos = 0;
                size_t pre = 0;

                while ((pos = name.find(SYSTEM_PATH_SEPARATOR, pre)) != std::string::npos) {
                    if (strcmp(name.substr(pre, (pos-pre)).c_str(), ".")!=0) {
                        ++ depth;
                        if (strcmp(name.substr(pre, (pos-pre)).c_str(), "..")==0)
                            depth -= 2;
                    }
                    pre = pos + 1;
                }
                return depth;
            }


            /*make @name, if @create_parent=true, non-exists parents will also be created*/
            static bool make_dir(const std::string &name, bool create_parent = false) {
                bool ret = true;
                if (!create_parent) {
                    if (!u::path::exists(name, u::D)) {
                        if (mkdir(name.c_str(), 0774) != 0) {
                            ret = false;
                        }
                    }
                } else {
                    size_t pos;
                    size_t prev = 0;
                    while (true) {
                        pos = name.find(SYSTEM_PATH_SEPARATOR, prev);
                        prev = pos + 1;
                        std::string sub = name.substr(0, pos);
                        if (!sub.empty() and !exists(sub, u::D)) {
                            if (mkdir(sub.c_str(), 0774) != 0) {
                                ret = false;
                                break;
                            }
                        }
                        if (pos == std::string::npos)
                            break;
                    }
                }

                return ret;
            }

            /*check if @name exists, if not @name will be created, if @create_parent=true, non-exists parents will also be created*/
            static bool confirm_dir(const std::string &name, bool create_parent = false) {
                bool ret = true;
                if (!path::exists(name, u::D)) {
                    ret = path::make_dir(name, create_parent);
                }
                return ret;
            }

            /*count how many files in @fold*/

            static size_t size(const std::string &filename, unsigned char option = u::F) {
#ifdef __STD_C11__
                auto count = [&option](const std::string &root, const std::string &parent, const std::string &name, unsigned char flag, size_t & _size)->bool {
                    if ((flag & option) == flag)
                        _size += 1;
                    return true;
                };
#endif
                size_t ret = 0;
                u::rsync_walk(filename, count, ret);
                return ret;
            }

    };

    class path_tree {
        private:
            char *_name;

            char *_parent;

            /*pointer to current file index*/
            std::list<char *>::iterator _current_file;

            /*pointer to current folder index*/
            std::list<path_tree *>::iterator _current_fold;

            /*pointer to point file lists contained in path node*/
            std::list<char *> _file_list;

            /*pointer to point sub folders contained in path node*/
            std::list<path_tree *> _fold_list;

            ~path_tree() {
                release();
            }

        public:

            /*check if @name included, @flag={u::F, u::D}*/
            bool exists(const std::string &name, unsigned char flag) {
                bool ret = false;
                if (flag == u::D) {
                    std::list<path_tree *>::iterator it = _fold_list.begin();
                    while (it != _fold_list.end()) {
                        if ((*it)->get("name") == name) {
                            ret = true;
                            break;
                        }
                        ++it;
                    }
                } else if (flag == u::F) {
                    std::list<char *>::iterator it = _file_list.begin();
                    while (it != _file_list.end()) {
                        if ((*it) == name) {
                            ret = true;
                            break;
                        }
                        ++it;
                    }
                }

                return ret;
            }

            path_tree() {
                _name = NULL;
                _parent = NULL;
                _current_file = _file_list.begin();
                _current_fold = _fold_list.begin();
            }

            /*return the true path, by combing parent and name*/
            char* abs() const {
                return u::path::join(_parent, _name, NULL);
            }

            /*get @part={'name','parent'}*/
            const char* get(const std::string &part = std::string("name")) const {
                const char *ret = NULL;
                if (part == "name") {
                    ret = _name;
                } else if (part == "parent") {
                    ret = _parent;
                }
                return ret;
            }

            /*set @part={'name', 'parent'} to @value*/
            void set(const std::string &value, const std::string &part = std::string("name")) {
                if (part == "name" && !value.empty()) {
                    u::string_free(&_name);
                    _name = u::string_dup(value);
                } else if (part == "parent" && !value.empty()) {
                    u::string_free(&_parent);
                    _parent = u::string_dup(value);
                }
            }

            /*add fold @fold to 'this' fold*/
            void add(path_tree *node) {
                if (node != NULL)
                    _fold_list.push_back(node);
            }

            /*add file @name to 'this' fold */
            void add(const std::string &name) {
                if (!name.empty()) {
                    _file_list.push_back(u::string_dup(name));
                }
            }

            /*get the begin iterator of file*/
            void begin(std::list<char *>::iterator &iter) {
                iter = _file_list.begin();
            }

            /*get the end iterator of file*/
            void end(std::list<char *>::iterator &iter) {
                iter = _file_list.end();
            }

            /*get the begin iterator of fold*/
            void begin(std::list<path_tree *>::iterator &iter) {
                iter = _fold_list.begin();
            }

            /*get the end iterator of fold*/
            void end(std::list<path_tree *>::iterator &iter) {
                iter = _fold_list.end();
            }

            /*get the next file name*/
            const char* next(std::list<char *>::iterator &iter) {
                const char* ret = NULL;
                if (iter != _file_list.end()) {
                    ret = (*iter);
                    ++iter;
                }
                return ret;
            }

            /*get the previous file name*/
            const char* prev(std::list<char *>::iterator &iter) {
                const char *ret = NULL;
                if (iter != _file_list.begin()) {
                    ret = (*iter);
                    --iter;
                }
                return ret;
            }

            /*get the next fold*/
            const path_tree* next(std::list<path_tree *>::iterator &iter) {
                const path_tree* ret = NULL;
                if (iter != _fold_list.end()) {
                    ret = (*iter);
                    ++iter;
                }
                return ret;
            }

            /*get the previous fold*/
            const path_tree* prev(std::list<path_tree *>::iterator &iter) {
                const path_tree *ret = NULL;
                if (iter != _fold_list.begin()) {
                    ret = (*iter);
                    --iter;
                }
                return ret;
            }

            /*get current value*/
            //template <typename Tv = char *>
            //const Tv current();

            /*count how many files and folders, @flag={u::F,u::D, u::F|u::D}*/
            size_t size(unsigned char flag = u::F) {
                size_t ret = 0;
                if ((flag & u::F) == u::F) {
                    ret += _file_list.size();
                }
                if ((flag & u::D) == u::D) {
                    ret += _fold_list.size();
                }
                std::list<path_tree*>::iterator it = _fold_list.begin();
                while (it != _fold_list.end()) {
                    ret += (*it)->size(flag);
                    ++it;
                }

                return ret;
            }

            /*release memory*/
            bool release() {
                u::string_free(&_name);
                u::string_free(&_parent);
                while (!_file_list.empty()) {
                    char* fp = _file_list.back();
                    _file_list.pop_back();
                    u::string_free(&fp);
                }

                while (!_fold_list.empty()) {
                    path_tree* ptp = _fold_list.back();
                    _fold_list.pop_back();
                    if (ptp != NULL) {
                        ptp->release();
                        delete ptp;
                        ptp = NULL;
                    }
                }
                return true;
            }

            /*print files and folders*/
            //void print();

            /*traverse the fold and operate each file with @fo and @user_data*/
            template <typename Tv, class file_operation>
                void walk(file_operation fo, Tv &user_data) {
                    std::list<char *>::iterator it = _file_list.begin();
                    while (it != _file_list.end()) {
                        char *parent = u::path::join(_parent, _name, NULL);
                        if (!fo(parent, (*it), user_data)) {
                            it = _file_list.end();
                        }
                        u::string_free(&parent);
                        ++it;
                    }
                    std::list<path_tree*>::iterator dit = _fold_list.begin();
                    while (dit != _fold_list.end()) {
                        (*dit)->walk(fo, user_data);
                        ++dit;
                    }
                }
    };

    typedef path* path_pointer;
    typedef path_tree* path_tree_pointer;

    bool contained(const std::string &value, const std::list<std::string> &exclude) {
        bool ret = false;
        std::list<std::string>::const_iterator it = exclude.begin();
        while (it != exclude.end()) {
            if (value == (*it)) {
                ret = true;
                break;
            }
            ++it;
        }
        return ret;
    }

    char *path_cat(const std::string &root, const std::string &parent, const std::string &filename) {
        char *ret = NULL;
        if (!root.empty()) {
            ret = u::string_dup(root);
        }
        if (!parent.empty()) {
            char * tmp = ret;
            ret = u::path::join(tmp, parent.c_str(), NULL);
            u::string_free(&tmp);
        }
        if (!filename.empty()) {
            char * tmp = ret;
            ret = u::path::join(tmp, filename.c_str(), NULL);
            u::string_free(&tmp);
        }
        return ret;
    }

    template <typename T, class file_op>
        bool rsync_walk(const std::string &root, file_op op, T &user_data, const std::string &parent ) {
#ifdef __GNUC__
            struct stat stbuf;
            struct dirent *dp;
            DIR *dfd;
            bool ret = true;
            char *dir = NULL;
            if (parent.empty())
                dir = u::string_dup(root);
            else
                dir = u::path::join(root.c_str(), parent.c_str(), NULL);
            if ((dfd = opendir(dir)) == NULL) {
                std::cout << "cannot open directory: " << dir << std::endl;
                ret = false;
                closedir(dfd);
            } else if (chdir(dir) != 0) {
                std::cout << "cannot enter directory: " << dir << std::endl;
                ret = false;
                size_t depth = path::depth(dir);
                std::string parent;
                while (depth != 0) {
                    parent.append("../");
                    --depth;
                }
                closedir(dfd);
            } else {
                while ((dp = readdir(dfd)) != NULL) {
                    if (stat(dp->d_name, &stbuf) == -1) {
                        std::cerr << "cannot access " << dp->d_name << "\n";
                        std::cerr << "warning: file " << dp->d_name << " will be escaped.\n";
                        continue;
                    }
                    if (strcmp(dp->d_name, ".") == 0 || strcmp(dp->d_name, "..") == 0) {
                        continue;
                    }
                    if ((stbuf.st_mode & S_IFMT) == S_IFREG) {
                        if (!op(root, parent, dp->d_name, u::F, user_data)) {
                            break;
                        }
                    } else if (S_ISDIR(stbuf.st_mode)) {
                        if (op(root, parent, dp->d_name, u::D, user_data)) {
                            char *name = NULL;
                            if (parent.empty())
                                name = u::string_dup(dp->d_name);
                            else
                                name = u::path::join(parent.c_str(), dp->d_name, NULL);
                            bool go_on = rsync_walk(root, op, user_data, name);
                            delete [] name;
                            if (!go_on)
                                break;
                        } else {
                            break;
                        }
                    }
                }
                closedir(dfd);
                if (chdir("../") != 0) {
                    std::cout << "cannot change directory to parent of " << dir << std::endl;
                    ret = false;
                }
            }
            u::string_free(&dir);
#else
            // for Microsoft C/C++ compiler
#endif

            return ret;
        }

    /*in order to use this feature, -lpthread option needed*/
#ifdef __STD_C11__

    template <typename T, class file_op>
        void async_walk(u::ws::work_station &ws, const std::string &root, file_op op, T &user_data, const std::string &parent = std::string()) { //unknow bug here, 乱码输出
#ifdef __GNUC__
            struct stat stbuf;
            struct dirent *dp;
            DIR *dfd;
            char *dir = NULL;
            if (parent.empty())
                dir = u::string_dup(root);
            else
                dir = u::path::join(root.c_str(), parent.c_str(), NULL);
            if ((dfd = opendir(dir)) == NULL) {
                std::cout << "cannot open directory: " << dir << std::endl;
                closedir(dfd);
            } else if (chdir(dir) != 0) {
                std::cout << "cannot enter directory: " << dir << std::endl;
                size_t depth = path::depth(dir);
                std::string parent;
                while (depth != 0) {
                    parent.append("../");
                    --depth;
                }
                closedir(dfd);
            } else {
                std::mutex mutex;
                while ((dp = readdir(dfd)) != NULL) {
                    if (stat(dp->d_name, &stbuf) == -1) {
                        std::cerr << "cannot access " << dp->d_name << "\n";
                        std::cerr << "warning: file " << dp->d_name << " will be escaped.\n";
                        continue;
                    }
                    if (strcmp(dp->d_name, ".") == 0 || strcmp(dp->d_name, "..") == 0) {
                        continue;
                    }
                    if ((stbuf.st_mode & S_IFMT) == S_IFREG) {
                        mutex.lock();
                        ws.run(op, std::string(root), std::string(parent), std::string(dp->d_name), u::F, user_data);
                        mutex.unlock();
                    } else if (S_ISDIR(stbuf.st_mode)) {
                        mutex.lock();
                        ws.run(op, std::string(root), std::string(parent), std::string(dp->d_name), u::D, user_data);
                        mutex.unlock();
                        char *name = NULL;
                        if (parent.empty())
                            name = u::string_dup(dp->d_name);
                        else
                            name = u::path::join(parent.c_str(), dp->d_name, NULL);
                        async_walk(ws, root, op, user_data, name);
                        delete [] name;
                    }
                }
                closedir(dfd);
                if (chdir("../") != 0) {
                    std::cout << "cannot change directory to parent of " << dir << std::endl;
                }
            }
            u::string_free(&dir);
#else
            // for Microsoft C/C++ compiler
#endif

        }
#endif

    template <typename T, class file_op>
        bool walk(size_t threads, const std::string &root, file_op op, T &user_data, const std::string &parent) {
            bool ret = true;
#ifdef __STD_C11__
            if (threads <= 1) {
                ret = rsync_walk(root, op, user_data, parent);
            } else {
                u::ws::work_station ws(threads);
                async_walk(ws, root, op, user_data, parent);
            }
#else
            ret = rsync_walk(root, op, user_data, parent);
#endif
            return ret;
        }

    bool begin_with(const std::string &name, bool ignore, ...) {
        bool ret = false;
        const char *value = NULL;
        va_list arg;
        va_start(arg, ignore);
        while (!ret) {
            value = va_arg(arg, const char*);
            if (value == NULL)
                break;
            ret = path::begin_with(name, value, ignore);
        }
        va_end(arg);
        return ret;
    }

    bool end_with(const std::string &name, bool ignore, ...) {
        bool ret = false;
        const char *value = NULL;
        va_list arg;
        va_start(arg, ignore);
        while (!ret) {
            value = va_arg(arg, const char*);
            if (value == NULL)
                break;
            ret = path::end_with(name, value, ignore);
        }
        va_end(arg);
        return ret;
    }

    template <typename Tv, class file_op>
        path_tree_pointer load_if(const std::string &root, file_op op, Tv &user_data) {
            u::path_tree_pointer ret = new u::path_tree();
            u::path _path = u::path(root);
            ret->set(_path.get("file"), "name");
            ret->set(_path.get("prefix"), "parent");
            _path.release();
#ifdef __GNUC__
            struct stat stbuf;
            struct dirent *dp;
            DIR *dfd;
            if ((dfd = opendir(root.c_str())) == NULL) {
                std::cout << "cannot open directory: " << root << std::endl;
                ret->release();
                ret = NULL;
                closedir(dfd);
            } else if (chdir(root.c_str()) != 0) {
                std::cout << "cannot enter directory: " << root << std::endl;
                ret->release();
                ret = NULL;
                size_t depth = path::depth(root);
                std::string parent;
                while (depth != 0) {
                    parent.append("../");
                    --depth;
                }
                closedir(dfd);
            } else {
                while ((dp = readdir(dfd)) != NULL) {
                    if (stat(dp->d_name, &stbuf) == -1) {
                        std::cerr << "cannot access " << dp->d_name << "\n";
                        std::cerr << "warning: file " << dp->d_name << " will be escaped.\n";
                        continue;
                    }
                    if (strcmp(dp->d_name, ".") == 0 || strcmp(dp->d_name, "..") == 0) {
                        continue;
                    }
                    if ((stbuf.st_mode & S_IFMT) == S_IFREG) {
                        if (op(root, dp->d_name, u::F, user_data))
                            ret->add(dp->d_name);
                    } else if (S_ISDIR(stbuf.st_mode)) {
                        if (op(root, dp->d_name, u::D, user_data)) {
                            char *name = u::path::join(root.c_str(), dp->d_name, NULL);
                            u::path_tree_pointer sub = load_if(name, op, user_data);
                            delete [] name;
                            if (sub != NULL)
                                ret->add(sub);
                        }
                    }
                }
                closedir(dfd);
                if (chdir("../") != 0) {
                    std::cout << "cannot change directory to parent of " << root << std::endl;
                    ret->release();
                    ret = NULL;
                }
            }
#else
            // for Microsoft C/C++ compiler
#endif
            return (ret);
        }

#ifndef __STD_C11__

    bool condition(const std::string &_parent, const std::string &_name, unsigned char flag, const std::list<std::string> &_exclude) {
        if ((flag & u::F) == u::F)
            return !contained(_name, _exclude);
        else return true;
    }
#endif
    path_tree_pointer load(const std::string & fold, const std::list<std::string> &excludes = std::list<std::string>()) {
#ifdef __STD_C11__
        auto condition = [](const std::string &_parent, const std::string &_name, unsigned char flag, const std::list<std::string> &_exclude)->bool {
            if ((flag & u::F) == u::F)
                return !contained(_name, _exclude);
            else return true;
        };
#endif
        return load_if(fold, condition, excludes);
    }

}

#endif
